package com.bestcem.xm.componet.filestorage.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.xuyanwu.spring.file.storage.Downloader;
import cn.xuyanwu.spring.file.storage.FileInfo;
import cn.xuyanwu.spring.file.storage.FileStorageProperties;
import cn.xuyanwu.spring.file.storage.FileStorageService;
import com.bestcem.xm.componet.filestorage.entity.FileDetail;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;

@Slf4j
@RequiredArgsConstructor
@Service
public class StorageService implements InitializingBean {

    private @NonNull FileStorageService fileStorageService;

    private @NonNull FileDetailService fileDetailService;

    private Table<String, String, String> platformVarTable;

    /**
     * 上传文件-通过文件对象
     *
     * @param file     上传文件对象
     * @param fileName 保存文件名
     * @param path     上传目录，如：images/
     * @param attr     保存属性，如哪个模块
     * @return 成功返回文件url
     */
    public FileInfo upload(File file, String fileName, String path, Dict attr) {
        return fileStorageService.of(file).setOriginalFilename(file.getName()).setSaveFilename(fileName).setPath(StrUtil.addSuffixIfNot(path, "/")).setAttr(attr).upload();
    }

    /**
     * 上传文件-通过文件流
     *
     * @param in       上传文件对象
     * @param fileName 保存文件名
     * @param path     上传目录，如：images/
     * @param attr     保存属性，如哪个模块
     * @return 成功返回文件url
     */
    public FileInfo upload(InputStream in, String fileName, String path, Dict attr) {
        return fileStorageService.of(in).setSaveFilename(fileName).setPath(StrUtil.addSuffixIfNot(path, "/")).setAttr(attr).upload();
    }


    /**
     * 上传文件-通过文件url
     *
     * @param url      文件url
     * @param fileName 保存文件名
     * @param path     上传目录，如：images/
     * @param attr     保存属性，如哪个模块
     * @return 成功返回文件url
     */
    public FileInfo upload(String url, String fileName, String path, Dict attr) {
        return fileStorageService.of(url).setSaveFilename(fileName).setPath(StrUtil.addSuffixIfNot(path, "/")).setAttr(attr).upload();
    }

    /**
     * 上传文件-通过字节数组
     *
     * @param bytes    字节数据
     * @param fileName 文件名
     * @param path     上传目录，如：images/
     * @return 成功返回文件url
     */
    public FileInfo upload(byte[] bytes, String fileName, String path, Dict attr) {
        return fileStorageService.of(bytes).setSaveFilename(fileName).setPath(StrUtil.addSuffixIfNot(path, "/")).setAttr(attr).upload();
    }

    public FileInfo upload(MultipartFile file, String fileName, String path, Dict attr) {
        return fileStorageService.of(file).setOriginalFilename(file.getOriginalFilename()).setSaveFilename(fileName).setPath(StrUtil.addSuffixIfNot(path, "/")).setAttr(attr).upload();
    }

    /**
     * 按平台取配置文件内容，比如我取minio的domain
     *
     * @param platform 平台代码
     * @param key      键值
     * @return java.lang.String
     * @author chenglong.yue <chenglong.yue@idiaoyan.com>
     * @date 2023/7/19 10:07
     */
    public String getPropertiesValueBy(String platform, String key) {
        if (StrUtil.isBlankIfStr(platform)) {
            platform = fileStorageService.getProperties().getDefaultPlatform();
        }
        String needFileName = StrUtil.toCamelCase(key, CharUtil.DASHED);
        if (platformVarTable.contains(platform, key)) {
            return platformVarTable.get(platform, key);
        }
        FileStorageProperties object = fileStorageService.getProperties();
        Field[] fields = ReflectUtil.getFields(object.getClass());
        String result = "";
        for (Field var : fields) {
            //List集合
            if (List.class.isAssignableFrom(var.getType())) {
                Type type = var.getGenericType();
                if (type instanceof ParameterizedType) {
                    if (!var.isAccessible()) {
                        var.setAccessible(true);
                    }
                    //获取到属性值的字节码
                    try {
                        Class<?> clzz = var.get(object).getClass();
                        //反射调用获取到list的size方法来获取到集合的大小
                        Method sizeMethod = clzz.getDeclaredMethod("size");
                        if (!sizeMethod.isAccessible()) {
                            sizeMethod.setAccessible(true);
                        }
                        //集合长度
                        int size = (int) sizeMethod.invoke(var.get(object));
                        //循环遍历获取到数据
                        for (int i = 0; i < size; i++) {
                            //反射获取到list的get方法
                            Method getMethod = clzz.getDeclaredMethod("get", int.class);
                            //调用get方法获取数据
                            if (!getMethod.isAccessible()) {
                                getMethod.setAccessible(true);
                            }
                            Object one = getMethod.invoke(var.get(object), i);
                            if (ReflectUtil.hasField(one.getClass(), "platform") && ReflectUtil.hasField(one.getClass(), needFileName)) {
                                String platformCode = ReflectUtil.getFieldValue(one, "platform").toString();
                                if (StrUtil.equals(platform, platformCode)) {
                                    Object fieldValue = ReflectUtil.getFieldValue(one, needFileName);
                                    if (fieldValue != null) {
                                        result = fieldValue.toString();
                                    }
                                    break;
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error("getPropertiesValueBy", e);
                    }
                }
            }
        }
        platformVarTable.put(platform, key, result);
        return result;
    }

    public FileInfo getByCondition(String platform, String path, String filename) {
        if (StrUtil.isBlankIfStr(platform)) {
            platform = fileStorageService.getProperties().getDefaultPlatform();
        }
        return fileDetailService.getByCondition(platform, path, filename);
    }

    public Downloader downloadByUrl(String url) {
        FileInfo fileInfo = fileDetailService.getByUrl(url);
        if (Objects.isNull(fileInfo)) {
            return null;
        }
        return fileStorageService.download(fileInfo);
    }

    public Downloader downloadByFileInfo(FileInfo fileInfo) {
        return fileStorageService.download(fileInfo);
    }


    public boolean deleteById(String id) {
        FileDetail fileDetail = fileDetailService.getById(id);
        if (Objects.nonNull(fileDetail)) {
            return fileStorageService.delete(BeanUtil.copyProperties(fileDetail, FileInfo.class, "attr"));
        }
        return false;
    }

    public boolean exists(String url) {
        FileInfo fileInfo = fileDetailService.getByUrl(url);
        if (Objects.isNull(fileInfo)) {
            return false;
        }
        return fileStorageService.exists(fileInfo);
    }

    @Override
    public void afterPropertiesSet() {
        platformVarTable = HashBasedTable.create();
        FileStorageProperties object = fileStorageService.getProperties();
        Field[] fields = ReflectUtil.getFields(object.getClass());
        for (Field var : fields) {
            //List集合
            if (List.class.isAssignableFrom(var.getType())) {
                Type type = var.getGenericType();
                if (type instanceof ParameterizedType) {
                    if (!var.isAccessible()) {
                        var.setAccessible(true);
                    }
                    //获取到属性值的字节码
                    try {
                        Class<?> clzz = var.get(object).getClass();
                        //反射调用获取到list的size方法来获取到集合的大小
                        Method sizeMethod = clzz.getDeclaredMethod("size");
                        if (!sizeMethod.isAccessible()) {
                            sizeMethod.setAccessible(true);
                        }
                        //集合长度
                        int size = (int) sizeMethod.invoke(var.get(object));
                        //循环遍历获取到数据
                        for (int i = 0; i < size; i++) {
                            //反射获取到list的get方法
                            Method getMethod = clzz.getDeclaredMethod("get", int.class);
                            //调用get方法获取数据
                            if (!getMethod.isAccessible()) {
                                getMethod.setAccessible(true);
                            }
                            Object one = getMethod.invoke(var.get(object), i);
                            if (!ReflectUtil.hasField(one.getClass(), "platform")) {
                                continue;
                            }
                            if (Objects.equals(ReflectUtil.getFieldValue(one, "enableStorage"), Boolean.FALSE)) {
                                continue;
                            }
                            String row = ReflectUtil.getFieldValue(one, "platform").toString();
                            Field[] fields4One = ReflectUtil.getFields(one.getClass());
                            for (Field f : fields4One) {
                                f.setAccessible(true);
                                String col = f.getName();
                                if (Objects.isNull(ReflectUtil.getFieldValue(one, f))) {
                                    continue;
                                }
                                String val = ReflectUtil.getFieldValue(one, f).toString();
                                platformVarTable.put(row, col, val);
                            }
                        }
                    } catch (Exception e) {
                        log.error("StorageService afterPropertiesSet", e);
                    }
                }
            }
        }
    }
}